2. 수치형 변수로 시작하는 생존율 분석#

앞서 우리는 타이타닉 데이터 파일들을 살펴보면서, 데이터의 크기와 첫 몇 행을 확인해보았습니다. 성별만으로도 76.555%의 정확도를 달성할 수 있다는 것도 알게 되었죠. 이제 한 걸음 더 나아가 데이터를 더 자세히 들여다 보겠습니다.

모델을 만들기 위해서는 먼저 데이터를 깊이 있게 이해하는 것이 중요합니다. 우리가 가진 train 데이터에는 어떤 패턴이 있을까요? 수치로 표현된 특성들(나이, 요금, 동승한 가족 수 등)은 생존과 어떤 관계가 있을까요? 이런 질문들에 대한 답을 찾아가면서, 자연스럽게 첫 번째 머신러닝 모델을 만들어 보겠습니다.

Note

아래에 있는 준비 코드는 이번 섹션의 코드 실행을 위해 필요한 이전 섹션의 코드입니다. 새로운 내용은 없으니 그대로 실행해주시면 됩니다. 이는 마치 요리 책에서 ‘이전 단계에서 준비해둔 반죽을 사용하여…’와 같은 설명과 비슷한 역할을 합니다.

💻 준비 코드

import pandas as pd

train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
submission = pd.read_csv('gender_submission.csv')

데이터의 기본 정보 확인#

데이터를 처음 마주했을 때 가장 먼저 해야 할 일은 데이터의 전반적인 특성을 파악하는 것입니다. pandas는 이를 위한 유용한 함수들을 제공합니다. 먼저 info() 함수를 통해 데이터의 기본 정보를 살펴보겠습니다.

💻 코드 & 실행결과

train.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  891 non-null    int64  
 1   Survived     891 non-null    int64  
 2   Pclass       891 non-null    int64  
 3   Name         891 non-null    object 
 4   Sex          891 non-null    object 
 5   Age          714 non-null    float64
 6   SibSp        891 non-null    int64  
 7   Parch        891 non-null    int64  
 8   Ticket       891 non-null    object 
 9   Fare         891 non-null    float64
 10  Cabin        204 non-null    object 
 11  Embarked     889 non-null    object 
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB

이 코드는 train 데이터의 전반적인 정보를 보여줍니다. 실행 결과를 보면 몇 가지 중요한 사실을 알 수 있습니다:

  • 총 891명의 승객 정보가 있습니다.

  • 대부분의 정보는 잘 갖춰져 있지만, 일부 정보가 빠져있습니다:

    • 나이(Age) 정보는 891명 중 714명만 있고, 177명의 정보가 빠져있습니다

    • 객실 번호(Cabin)는 더 많은 정보가 빠져있어서, 891명 중 204명의 정보만 있습니다

  • 정보의 형태도 다양합니다:

    • 숫자로 된 정보: 나이, 요금, 동승자 수 등

    • 글자로 된 정보: 이름, 성별, 객실 번호 등

이처럼 빠진 데이터(결측치)가 있다는 것은 나중에 특별한 처리가 필요하다는 신호입니다. 특히 나이 정보가 꽤 많이 빠져있네요. 이 부분은 나중에 자세히 다루어보도록 하겠습니다.

숫자로 된 정보들의 특징 파악#

이제 train 데이터의 통계적 특성을 살펴보겠습니다. describe() 함수는 수치형 데이터의 기본 통계량을 계산해줍니다.

💻 코드 & 실행결과

train.describe()
PassengerId Survived Pclass Age SibSp Parch Fare
count 891.000000 891.000000 891.000000 714.000000 891.000000 891.000000 891.000000
mean 446.000000 0.383838 2.308642 29.699118 0.523008 0.381594 32.204208
std 257.353842 0.486592 0.836071 14.526497 1.102743 0.806057 49.693429
min 1.000000 0.000000 1.000000 0.420000 0.000000 0.000000 0.000000
25% 223.500000 0.000000 2.000000 20.125000 0.000000 0.000000 7.910400
50% 446.000000 0.000000 3.000000 28.000000 0.000000 0.000000 14.454200
75% 668.500000 1.000000 3.000000 38.000000 1.000000 0.000000 31.000000
max 891.000000 1.000000 3.000000 80.000000 8.000000 6.000000 512.329200

describe() 함수는 각 수치형 컬럼에 대해 다음과 같은 정보를 제공합니다:

  • 생존율: 전체 승객의 38.4%가 생존했습니다 (mean = 0.384)

  • 나이 분포:

    • 평균 나이는 29.7세입니다

    • 가장 어린 승객은 0세(아기)였고, 가장 나이 많은 승객은 80세였습니다

    • 절반의 승객들이 20세에서 38세 사이였습니다

  • 요금 분포:

    • 평균 요금은 32.2파운드였습니다

    • 무료로 탑승한 승객부터 512.3파운드를 지불한 승객까지 있었네요

    • 요금의 차이가 매우 큰 것으로 보아, 객실 등급에 따라 요금이 크게 달랐던 것 같습니다

글자로 된 정보들의 특징 파악#

문자열(object) 타입 컬럼들의 특성도 살펴보겠습니다. describe() 함수에 include=’object’ 파라미터를 추가하면 문자열 데이터에 대한 기본 통계를 확인할 수 있습니다.

💻 코드 & 실행결과

train.describe(include='object')
Name Sex Ticket Cabin Embarked
count 891 891 891 204 889
unique 891 2 681 147 3
top Braund, Mr. Owen Harris male 347082 B96 B98 S
freq 1 577 7 4 644

문자열 데이터에 대해서는 다른 종류의 통계가 제공됩니다:

  • count: 결측치가 아닌 값의 개수

  • unique: 고유한 값의 개수

  • top: 가장 많이 등장하는 값

  • freq: top 값의 등장 횟수

실행 결과를 보면:

  • 성별(Sex)은 ‘male’과 ‘female’ 두 가지 값만 있습니다

  • 승선 항구(Embarked)는 대부분 ‘S’(Southampton)입니다

  • 객실 번호(Cabin)는 종류가 매우 다양하고, 같은 번호를 쓰는 경우도 거의 없네요

  • 이름(Name)은 당연히 모두 다릅니다

Note

코드 생성에 관한 중요한 안내

프롬프트를 통해 코드를 생성할 때 알아두면 좋을 점들입니다:

  • 이 책에 있는 프롬프트를 그대로 입력하더라도 책과 완전히 동일한 코드가 생성되지 않을 수 있습니다. 이는 자연스러운 현상입니다:

    • 사용하는 인공지능 모델에 따라 다른 코드가 생성될 수 있습니다

    • 같은 모델이라도 버전이 다르면 다른 코드를 제시할 수 있습니다

    • 심지어 같은 모델, 같은 버전이라도 매번 다른 코드를 생성할 수 있습니다

  • 생성된 코드가 책의 코드와 다르더라도, 실행 결과는 책의 내용을 이해하는 데 무리가 없습니다

  • 책의 설명을 더 명확하게 하기 위해 프롬프트나 코드를 일부 수정한 경우도 있습니다

  • 원하신다면 책에 있는 코드를 그대로 복사해서 사용하셔도 됩니다

결과적으로 동일한 분석 목표를 달성할 수 있다면, 어떤 방식을 선택하시든 무방합니다

생존율 시각화하기#

이제 전체 승객 중 생존자의 비율이 어떻게 되는지 한눈에 알아보기 쉽게 시각화해 보겠습니다. 파이그래프를 사용하면 생존자와 사망자의 비율을 직관적으로 파악할 수 있습니다.

📝 프롬프트

전체 승객 중에서 생존자가  명이나 되는지 파이그래프로 보여줘

💻 코드 & 실행결과

import matplotlib.pyplot as plt

survival_counts = train['Survived'].value_counts()
plt.figure(figsize=(6, 6))
plt.pie(survival_counts, labels=['Deceased', 'Survived'], autopct='%1.1f%%', startangle=90)
plt.title('Survival Rate')
plt.show()
../_images/c0fb1185a21bb1d35aff399a345caa18e39cd9cebd63a82dd65ef1c0096410c6.png

파이그래프를 보면 승객들의 생존 여부를 한눈에 파악할 수 있습니다:

  • 전체 승객 중 61.6%가 사망했습니다

  • 생존한 승객은 38.4%입니다

이는 앞서 describe() 함수로 확인했던 생존률과 정확히 일치합니다. 시각화를 통해 보니 생존자가 사망자보다 훨씬 적다는 것을 더 직관적으로 이해할 수 있네요.

숫자로 된 정보들 사이의 관계 분석#

나이, 요금, 객실 등급 같은 숫자로 된 정보들이 서로 어떤 관계를 가지고 있는지 알아보겠습니다. 이러한 관계를 시각화하는 좋은 방법 중 하나가 ‘히트맵(heatmap)’입니다. 히트맵은 두 정보 사이의 상관관계를 색상으로 표현합니다:

  • 1에 가까울수록 강한 양의 상관관계 (한 특성이 증가하면 다른 특성도 증가)

  • -1에 가까울수록 강한 음의 상관관계 (한 특성이 증가하면 다른 특성은 감소)

  • 0에 가까울수록 상관관계가 약함 (두 특성이 거의 관련이 없음)

이러한 히트맵을 통해 어떤 정보들이 서로 관련이 있는지, 특히 생존과는 어떤 정보가 강한 관계를 가지는지 한눈에 파악할 수 있습니다.

📝 프롬프트

숫자로  정보들 사이의 관계를 보여주는 히트맵을 그려줘

💻 코드 & 실행결과

import seaborn as sns

numeric_cols = train.select_dtypes(include=['number']).columns
corr_matrix = train[numeric_cols].corr()

plt.figure(figsize=(10, 8))
sns.heatmap(corr_matrix, 
            annot=True,        # 상관계수 숫자 표시
            center=0,          # 0을 중심으로 색상 표시
            cmap='coolwarm',   # 파란색-흰색-빨간색 색상맵 사용
            fmt='.2f',         # 소수점 둘째자리까지 표시
            square=True,       # 정사각형 형태로 표시
            vmax=1,            # 최대값 설정
            vmin=-1)           # 최소값 설정
plt.title('Correlation between Numeric Features')
plt.show()
../_images/8668b0faa5da5049195117bf0e96433e2f49fa24734c99b043082058bf209c84.png

생존(Survived)과 다른 정보들의 관계를 보면:

  • 객실 등급(Pclass)과는 뚜렷한 음의 관계(-0.34): 등급이 높을수록(숫자가 작을수록) 생존 확률이 높았습니다

  • 요금(Fare)과는 뚜렷한 양의 관계(0.26): 비싼 요금을 낸 승객일수록 생존 확률이 높았습니다

  • 나이(Age)와는 거의 관계가 없음(-0.07): 흰색에 가까운 색상이 이를 잘 보여줍니다

이러한 시각화를 통해 우리는 어떤 특성이 생존과 강한 관계가 있는지 한눈에 파악할 수 있습니다. 특히 객실 등급과 요금은 생존과 뚜렷한 관계가 있어, 이 정보들이 생존 예측에 유용할 것으로 보입니다.

객실 등급별 생존률 분석#

상관계수를 통해 객실 등급(Pclass)이 생존 여부와 꽤 강한 관련성이 있다는 것을 확인했습니다. 이 관계를 시각화해서 좀 더 자세히 살펴보겠습니다.

📝 프롬프트

객실 등급(1등석, 2등석, 3등석) 따라 생존률이 어떻게 다른지 막대그래프로 보여줘

💻 코드 & 실행결과

pclass_survival = train.groupby('Pclass')['Survived'].mean()

plt.figure(figsize=(8, 6))
sns.barplot(x=pclass_survival.index, y=pclass_survival.values)
plt.title('Survival Rate by Passenger Class')
plt.xlabel('Passenger Class')
plt.ylabel('Survival Rate')
plt.xticks(ticks=[0,1,2], labels=['1st','2nd','3rd'])
plt.show()
../_images/c31cab89a148c68375f9df9dae6335b8b723ef21380ded3403a6d3ffdfee7d07.png

위 그래프에서는 정확한 생존률 값을 파악하기가 어렵습니다. Plotly를 사용하면 마우스를 막대 위에 올렸을 때 정확한 수치를 보여주는 인터랙티브한 그래프를 만들 수 있습니다. 이를 통해 각 객실 등급별 생존률을 더 정확히 살펴보겠습니다.

📝 프롬프트

Plotly를 사용해서 train 데이터의 객실 등급별 생존률을 막대그래프로 보여줘

💻 코드 & 실행결과

import plotly.express as px

# Pclass별 생존률 계산
survival_rates = train.groupby("Pclass")["Survived"].mean().reset_index()

# Plotly를 사용한 막대 그래프 생성
fig = px.bar(
    survival_rates,
    x="Pclass",
    y="Survived",
    title="Pclass별 생존률",
    labels={"Pclass": "객실 등급", "Survived": "생존률"},
)

# X축을 category 타입으로 설정하여 1, 2, 3만 표시
fig.update_layout(xaxis=dict(type="category"))

# 그래프 표시
fig.show()

그래프를 보면 객실 등급에 따른 생존률의 차이가 매우 뚜렷합니다. 각 막대 위에 마우스를 올려보면 정확한 생존률을 확인할 수 있습니다:

  • 1등석 승객의 생존률이 약 63%로 가장 높습니다

  • 2등석 승객의 생존률은 약 47%입니다

  • 3등석 승객의 생존률은 약 24%로, 2등석 승객의 절반 정도밖에 안 됩니다


지금까지 우리는 타이타닉 생존자 데이터를 다양한 방법으로 살펴보았습니다. 수치형 데이터의 분포와 관계를 파악하고, 특히 객실 등급과 생존률의 관계를 자세히 분석했습니다.
다음 섹션에서는 이렇게 분석한 데이터를 바탕으로 첫 번째 생존자 예측 모델을 만들어보겠습니다. 비록 단순한 모델이지만, 이를 통해 머신러닝의 기본적인 과정을 이해할 수 있을 것입니다. 또한 모델의 성능을 평가하고, 어떻게 하면 더 나은 예측을 할 수 있을지 고민해보는 시간도 가져보겠습니다.

직접 해보기

위에서 살펴본 것처럼, 다른 수치형 특성들도 생존률과 관련이 있을까요? 아래 특성들에 대해서도 직접 시각화해보면 재미있는 패턴을 발견할 수 있을 것입니다:

  • Age(나이)와 생존률의 관계

  • Fare(요금)과 생존률의 관계

  • SibSp(형제자매 수)와 생존률의 관계

  • Parch(부모자녀 수)와 생존률의 관계

데이터 속 숨은 이야기

타이타닉호의 최고가 승객: Charlotte Cardeza

데이터에서 가장 비싼 요금인 512파운드(현재 가치로 약 1억 3천만원)를 지불한 승객은 Charlotte Cardeza였습니다. 그녀는 펜실베니아 저먼타운의 호화로운 저택 ‘Montebello’에 거주하던 58세의 부유한 미국인이었습니다.

Charlotte은 아들 Thomas와 메이드 Anna Ward와 함께 Cherbourg에서 타이타닉호에 탑승했습니다. 그들이 사용한 B51/53/55번 객실은 배에서 가장 비싼 스위트룸 중 하나였습니다. 그녀의 일행은 무려 14개의 트렁크, 4개의 여행가방, 3개의 화물 상자를 가지고 탑승했는데, 후에 이 분실 물품들에 대해 36,567파운드의 보상을 청구했다고 합니다. 다행히 Charlotte과 그녀의 일행은 3번 구명보트를 타고 무사히 구조되었습니다.

그녀의 사례는 타이타닉호 1등실의 최상층 서비스가 어떠했는지, 그리고 당시 상류층의 호화로운 여행 문화를 잘 보여줍니다.